package com.hmdp.utils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hmdp.entity.Shop;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.function.Function;

//缓存工具类
@Slf4j
@Component
public class CacheClient {
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     *
     * @param key 键
     * @param value 值
     * @param time 过期时间
     * @param timeunit 时间单位
     */
    public void set(String key , Object value , Long time, TimeUnit timeunit){
        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, timeunit);
    }
    //加锁
    private boolean getLock(String key)
    {
        Boolean b = redisTemplate.opsForValue().setIfAbsent(key, "1", 30, TimeUnit.SECONDS);
        return b;
    }
    //解锁
    private void unLock(String key)
    {
        redisTemplate.delete(key);
    }
    //互斥锁

    /**
     *
     * @param keyPrefix redis前缀
     * @param id    ID
     * @param type  查询类型
     * @param function  从数据库查询的函数
     * @param time  过期时间
     * @param timeunit  时间单位
     * @return
     * @param <R>   返回值类型
     * @param <ID>  主键类型
     */
    public <R,ID> R queryWithMutex(String keyPrefix, ID id, Class<R> type, Function<ID,R> function,Long time, TimeUnit timeunit)
    {
        String lockKey=keyPrefix+id;
        String JSONobj  = (String) redisTemplate.opsForValue().get(lockKey);
        //如果查询到缓存的店铺直接返回
        if(!StrUtil.isBlank(JSONobj))
        {
            return JSONUtil.toBean(JSONobj, type);
        }
        //存储的店铺没有信息返回空
        if(JSONobj!=null&&"".equals(JSONobj))
        {
            return null;
        }
        //尝试获取锁
        boolean lock = getLock(lockKey);
        R r = null;
        //有其他线程操作
        try {
            if(!lock)
            {
                Thread.sleep(50);
                return queryWithMutex(keyPrefix, id, type, function, time, timeunit);
            }
            //查询数据库
            r = function.apply(id);
            if(r==null)
            {
                this.set(lockKey,"",time,timeunit);
                return null;
            }
            this.set("cache:shop:"+id,JSONUtil.toJsonStr(r),time,timeunit);
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        finally {
            unLock(lockKey);
        }
        return r;

    }
}
